package cn.jbolt.core.service;

import cn.jbolt.core.base.JBoltMsg;
import cn.jbolt.core.base.config.JBoltConfig;
import cn.jbolt.core.bean.JsTreeBean;
import cn.jbolt.core.cache.JBoltGlobalConfigCache;
import cn.jbolt.core.cache.JBoltRoleCache;
import cn.jbolt.core.cache.JBoltUserCache;
import cn.jbolt.core.common.enums.JBoltLoginState;
import cn.jbolt.core.common.enums.JBoltSystemLogTargetType;
import cn.jbolt.core.common.enums.JBoltSystemLogType;
import cn.jbolt.core.db.sql.Sql;
import cn.jbolt.core.db.util.JBoltDbKit;
import cn.jbolt.core.kit.JBoltSaasTenantKit;
import cn.jbolt.core.kit.JBoltUserKit;
import cn.jbolt.core.model.*;
import cn.jbolt.core.service.base.JBoltBaseService;
import cn.jbolt.core.util.JBoltArrayUtil;
import cn.jbolt.core.util.JBoltDateUtil;
import com.jfinal.aop.Inject;
import com.jfinal.kit.*;
import com.jfinal.plugin.activerecord.Page;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 用户Service
 *
 * @ClassName: UserService
 * @author: JFinal学院-小木 QQ：909854136
 * @date: 2020年5月2日
 */
public class JBoltUserService extends JBoltBaseService<User> {
	protected User dao = new User().dao();
	@Inject
	protected JBoltOnlineUserService jboltOnlineUserService;
	@Inject
	protected JBoltLoginLogService jboltLoginLogService;
	@Inject
	protected JBoltRemoteLoginLogService jboltRemoteLoginLogService;
	@Inject
	protected JBoltDeptService jBoltDeptService;

	@Override
	protected User dao() {
		return dao;
	}

	/**
	 * 获取所有的角色组
	 *
	 * @return
	 */
	public List<String> getAllRoleGroups() {
		return query(selectSql().distinct("roles"));
	}

	/**
	 * 随机获取一个
	 *
	 * @return
	 */
	public User getOneSystemAdmin() {
		return getRandomOne(Okv.by("is_system_admin", TRUE));
	}

	/**
	 * 检测一个角色是否被用户使用了
	 * @param id
	 * @return
	 */
	public boolean checkRoleInUse(Long id) {
		Sql sql=selectSql().selectId().findInSet(id, "roles",true);
		Long existId=queryLong(sql);
		return isOk(existId);
	}
	/**
	 * 切换Enable状态
	 * @param id
	 * @return
	 */
	public Ret toggleEnable(Long id) {
		Long userId = JBoltUserKit.getUserId();
		if(userId.equals(id)){
			return fail("自己不能操作自己的用户状态");
		}
		Ret ret=toggleBoolean(id,Kv.by("userId", userId), "enable");
		if(ret.isOk()){
			User user=ret.getAs("data");
			addUpdateSystemLog(id, userId,user.getName(), "的状态为["+(user.getEnable()?"启用]":"禁用]"));
			return successWithData(user.getEnable());
		}

		return ret;
	}
	@Override
	public String checkCanToggle(User model, String column,Kv kv) {
		if(column.equals("enable")) {
			if(model.getIsSystemAdmin()&&model.getEnable()) {
				return "不能禁用超级管理员";
			}
		}
		return null;
	}
	/**
	 * 通过用户名密码获取用户信息
	 * @param userName
	 * @param password 这个密码是前端经过md5 和 前后+特定字符串 加密后传输过来的
	 * @return
	 */
	public Ret getUser(String userName, String password) {
		if(hasNotOk(userName,password)){
			return fail(JBoltLoginState.USERNAME_PWD_ERROR.getText());
		}
		//通过用户名去找用户
		User user=findByUserNameWithPwdInfo(userName);
		if(user==null||notOk(user.getPwdSalt())) {return fail("用户信息异常，账号密码设置有误");}
		String userPassword=user.getPassword();
		String hashPassword=calFrontendPasswordWithSalt(password,user.getPwdSalt());
		if(!hashPassword.equals(userPassword)) {return fail(JBoltLoginState.USERNAME_PWD_ERROR.getText());}
		return successWithData(user);
	}

	private User findByUserNameWithPwdInfo(String userName) {
		return findFirst(selectSql().eq("username", userName));
	}

	/**
	 * 更新密码
	 * @param originPwd
	 * @param newPwd
	 * @return
	 */
	public Ret updatePwd(String originPwd, String newPwd) {
		if(notOk(originPwd)||notOk(newPwd)) {return fail(JBoltMsg.PARAM_ERROR); }
		if(originPwd.length()!=originPwd.trim().length()) {return fail("原密码不能输入空格");}
		if(newPwd.length()!=newPwd.trim().length()) {return fail("新密码不能输入空格");}
		if(originPwd.length()>128){
			return fail("原密码不能超过128位");
		}
		if(newPwd.length()>128){
			return fail("新密码不能超过128位");
		}
		Long userId = JBoltUserKit.getUserId();
		User me=superFindById(userId);
		if(me==null){return fail("用户信息异常");}
		Ret ret = getUser(me.getUsername(), originPwd);
		User user = ret.isFail()?null:ret.getAs("data");
		if(user==null){
			return fail("原密码输入不正确");
		}
		if(user.getId().intValue()!=userId.intValue()){
			return fail("原密码输入不正确");
		}
		user.setPassword(calFrontendPasswordWithSalt(newPwd, user.getPwdSalt()));
		user.setLastPwdUpdateTime(new Date());
		boolean success=user.update();
		return ret(success);
	}
	/**
	 * 修改密码
	 * @param userId
	 * @param newPwd
	 * @return
	 */
	public Ret resetPwd(Long userId,String newPwd) {
		if(notOk(newPwd)){
			return fail(JBoltMsg.PARAM_ERROR);
		}
		if(newPwd.length()>40){
			return fail("新密码不能超过40位");
		}
		Long sessionUserId = JBoltUserKit.getUserId();
		User user=superFindById(userId);
		if(user==null){return fail("用户不存在");}
		if(user.getIsSystemAdmin()&& !userId.equals(sessionUserId)) {return fail("您无权初始化系统超管员密码");}
		user.setPassword(calPasswordWithSalt(newPwd, user.getPwdSalt()));
		user.setLastPwdUpdateTime(new Date());
		boolean success=user.update();
		if(success){
			addUpdateSystemLog(userId, sessionUserId,user.getName(), "的[密码]");
		}
		return ret(success);
	}
	/**
	 * 将密码明文进行加盐加密
	 * @param pwd
	 * @param pwdSalt
	 * @return
	 */
	public String calPasswordWithSalt(String pwd,String pwdSalt) {
		return HashKit.sha256(pwdSalt+HashKit.md5(pwd));
	}

	/**
	 * 将密码明文进行加盐加密
	 * @param pwd 密码传过来就是md5加密后 并且插入字符的样式
	 * @param pwdSalt
	 * @return
	 */
	public String calFrontendPasswordWithSalt(String pwd,String pwdSalt) {
		if(hasNotOk(pwd,pwdSalt) || pwd.trim().length()<=5){
			throw new RuntimeException("密码不正确");
		}
		return HashKit.sha256(pwdSalt+pwd.substring(2,pwd.length()-3));
	}

	/**
	 * 检测密码是否准确
	 * @param userId
	 * @param password
	 * @return
	 */
	public boolean checkPwd(Long userId,String password) {
		if(notOk(password)) {return false;}
		if(password.length()!=password.trim().length()) {return false;}
		User user=superFindById(userId);
		if(user==null) {
			return false;
		}
		String userPassword=user.getPassword();
		String hashPassword=calFrontendPasswordWithSalt(password,user.getPwdSalt());
		return hashPassword.equals(userPassword);
	}
	/**
	 * 得到查询指定角色下的用户列表的sql
	 * @param roleId
	 * @return
	 */
	public Sql getUsersByRoleIdSql(Long roleId) {
		Sql sql=selectSql();
		//如果不是超管 就只能查询不是超管的用户
		if(!JBoltUserKit.isSystemAdmin()) {
			sql.eq("is_system_admin", FALSE);
		}
		sql.select(getTableSelectColumnsWithout("password","pwd_salt"));
		sql.findInSet(roleId, "roles", true);
		return sql;
	}
	/**
	 * 查询指定角色下的用户列表
	 * @param roleId
	 * @return
	 */
	public List<User> getUsersByRoleId(Long roleId) {
		return find(getUsersByRoleIdSql(roleId));
	}
	/**
	 * 查询指定角色下的用户列表
	 * @param roleId
	 * @return
	 */
	public Page<User> paginateUsersByRoleId(int pageNumber,int pageSize,Long roleId) {
		if(notOk(roleId)){
			return emptyPage(pageSize);
		}
		Sql sql=getUsersByRoleIdSql(roleId);
		sql.page(pageNumber, pageSize);
		return paginate(sql);
	}
	/**
	  * 删除用户角色
	 * @param userId
	 * @param roleId
	 * @return
	 */
	public Ret deleteUserRole(Long userId,Long roleId) {
		if(notOk(userId)||notOk(roleId)) {return fail(JBoltMsg.PARAM_ERROR);}
		User sessionUser=JBoltUserKit.getUser();
		if(sessionUser==null) {return fail(JBoltMsg.PARAM_ERROR);}
		//判断操作人和操作对象不是同一个人
		if(sessionUser.getId().intValue()==userId.intValue()&&!sessionUser.getIsSystemAdmin()) {
			return fail("系统禁止操作设置自身角色");
		}
		//得到被操作User
		User user=findById(userId);
		if(user==null) {return fail("用户 "+JBoltMsg.DATA_NOT_EXIST);}
		Role role=JBoltRoleCache.me.get(roleId);
		if(role==null) {return fail("角色 "+JBoltMsg.DATA_NOT_EXIST);}
		//拿到user的角色 删掉指定roleId
		String roles=user.getRoles();
		if(notOk(roles)) {return fail("此用户未设置任何角色");}
		String newRoles=(","+roles+",");
		if(!newRoles.contains("," + roleId + ",")) {return fail("用户未绑定此角色");}
		newRoles=newRoles.replace(","+roleId+",", ",");
		if(newRoles.equals(",")) {
			newRoles=null;
		}else {
			newRoles=newRoles.substring(1, newRoles.length()-1);
		}
		user.setRoles(newRoles);
		boolean success=user.update();
		if(success) {
			addUpdateSystemLog(userId, JBoltUserKit.getUserId(), user.getName(),"，将其从角色【"+role.getName()+"】中删除");
		}
		return ret(success);
	}
	/**
	 * 根据roleId，清空角色下的用户列表
	 * 只是删除绑定，并不是删掉用户信息
	 * @param roleId
	 * @return
	 */
	public Ret clearUsersByRole(Long roleId) {
		if(notOk(roleId)) {return fail(JBoltMsg.PARAM_ERROR);}
		User sessionUser=JBoltUserKit.getUser();
		if(sessionUser==null) {return fail(JBoltMsg.PARAM_ERROR);}
		if(!sessionUser.getIsSystemAdmin()) {return fail("只有内置系统超级管理员才有权执行此操作");}
		Role role=JBoltRoleCache.me.get(roleId);
		if(role==null) {return fail("角色 "+JBoltMsg.DATA_NOT_EXIST);}
		List<User> users=getUsersByRoleId(roleId);
		if(notOk(users)) {return fail("此角色下已无用户");}
		users.forEach(user->processDeleteUserRole(user,roleId));
		batchUpdate(users);
		addSystemLog(roleId, JBoltUserKit.getUserId(), JBoltSystemLogType.UPDATE.getValue(), JBoltSystemLogTargetType.ROLE.getValue(), role.getName(),"，将其用户列表清空");
		return SUCCESS;
	}
	/**
	 * 处理用户的角色
	 * @param user
	 * @param roleId
	 */
	private void processDeleteUserRole(User user, Long roleId) {
		//拿到user的角色 删掉指定roleId
		String roles=user.getRoles();
		if(notOk(roles)) {return;}
		String newRoles=(","+roles+",");
		if(!newRoles.contains("," + roleId + ",")) {return;}
		newRoles=newRoles.replace(","+roleId+",", ",");
		if(newRoles.equals(",")) {
			newRoles=null;
		}else {
			newRoles=newRoles.substring(1, newRoles.length()-1);
		}
		user.setRoles(newRoles);
	}



	/**
	 * 更新avatar
	 * @param userId
	 * @param url
	 * @return
	 */
	public Ret updateUserAvatar(Long userId, String url) {
		User user=findById(userId);
		if(user==null){return fail("用户信息异常");}
		user.setAvatar(url);
		boolean success=user.update();
		return ret(success);
	}
	/**
	 * 处理异地登录异常
	 * @param user
	 * @param log
	 */
	public boolean processUserRemoteLogin(User user, LoginLog log) {
		//判断异地登录
		boolean isRemoteLogin=checkIsRemoteLogin(user,log);
		if(isRemoteLogin) {
			jboltLoginLogService.updateIsRemoteLogin(log,isRemoteLogin);
			//处理异地登录异常日志
			jboltRemoteLoginLogService.createLog(user,log);
		}
		return isRemoteLogin;
	}

	/**
	 * 判断是否异地登录
	 * @param user
	 * @param log
	 * @return
	 */
	private boolean checkIsRemoteLogin(User user, LoginLog log) {
		if(notOk(user.getLoginIp()) || notOk(user.getLoginCountry()) || notOk(user.getLoginProvince()) || notOk(user.getLoginCity())) {return false;}
		return !user.getLoginCountry().equals(log.getLoginCountry()) || !user.getLoginProvince().equals(log.getLoginProvince()) || !user.getLoginCity().equals(log.getLoginCity());
	}

	/**
	  * 更新用户登录信息
	 * @param user
	 * @param log
	 */
	public void processUserLoginInfo(User user,boolean isRemoteLogin, LoginLog log) {
		//更新登录信息
		user.setLoginIp(log.getLoginIp());
		user.setLoginCountry(log.getLoginCountry());
		user.setLoginProvince(log.getLoginProvince());
		user.setLoginCity(log.getLoginCity());
		user.setLoginTime(log.getLoginTime());
		user.setIsRemoteLogin(isRemoteLogin);
		if(user.getLastPwdUpdateTime() == null){
			user.setLastPwdUpdateTime(user.getCreateTime());
		}
		user.update();
	}

	/**
	 * 检测是否存在超管员
	 * @return
	 */
	public boolean checkExistSuperAdmin() {
		return exists("is_system_admin", TRUE);
	}
	/**
	 * 检测部门是否被用户绑定使用
	 * @param deptId
	 * @return
	 */
	public boolean checkDeptInUse(Long deptId) {
		return exists("dept_id", deptId);
	}

	/**
	 * 检测用户分配了指定的所有角色
	 *
	 * @param userId
	 * @param checkAll
	 * @param roleIds
	 * @return
	 */
	public boolean checkUserHasRole(Long userId, boolean checkAll, Long... roleIds) {
		if (roleIds == null || roleIds.length == 0) {
			return false;
		}
		User user = JBoltUserCache.me.get(userId);
		if (user == null) {
			return false;
		}
		String roles = user.getRoles();
		if (StrKit.isBlank(roles)) {
			return false;
		}
		String[] roleArray = JBoltArrayUtil.from(roles, ",");
		if (roleArray.length == 0) {
			return false;
		}
		boolean success = false, result = false;
		int count = roleIds.length;
		for (Long roleId : roleIds) {
			success = JBoltArrayUtil.contains(roleArray, roleId);
			if (checkAll && !success) {
				break;
			}
			// 只有一个 并且sccess 返回true
			if (count == 1 && success) {
				result = true;
				break;
			}
			// 如果是只要任何一个存在就返回True
			if (!checkAll && success) {
				result = true;
				break;
			}
		}
		return result;
	}

	/**
	 * 检测用户分配了指定的所有角色
	 *
	 * @param userId
	 * @param checkAll
	 * @param roleSns
	 * @return
	 */
	public boolean checkUserHasRoleSn(Long userId, boolean checkAll, String... roleSns) {
		if (roleSns == null || roleSns.length == 0) {
			return false;
		}
		User user = JBoltUserCache.me.get(userId);
		if (user == null) {
			return false;
		}
		String roles = user.getRoles();
		if (StrKit.isBlank(roles)) {
			return false;
		}
		String[] roleArray = JBoltArrayUtil.from(roles, ",");
		if (roleArray.length == 0) {
			return false;
		}
		List<Long> roleIds = null;
		if(roleSns.length == 1 && roleSns[0].contains(",")){
			roleIds = JBoltRoleCache.me.getIdsBySns(JBoltArrayUtil.from3(roleSns[0]));
		}else{
			roleIds = JBoltRoleCache.me.getIdsBySns(roleSns);
		}
		if (notOk(roleIds)) {
			return false;
		}
		boolean success = false, result = false;
		int count = roleIds.size();
		for (Long roleId : roleIds) {
			success = JBoltArrayUtil.contains(roleArray, roleId);
			if (checkAll && !success) {
				break;
			}
			// 只有一个 并且sccess 返回true
			if (count == 1 && success) {
				result = true;
				break;
			}
			// 如果是只要任何一个存在就返回True
			if (!checkAll && success) {
				result = true;
				break;
			}
		}
		return result;
	}

	@Override
	protected int systemLogTargetType() {
		return JBoltSystemLogTargetType.USER.getValue();
	}

	/**
	 * 获得部门人员树
	 * @param openLevel
	 * @param rootName
	 * @param selectUserId
	 * @return
	 */
	public List<JsTreeBean> getDeptUserTree(int openLevel,String rootName,Long selectUserId){
		List<Dept> depts = jBoltDeptService.getTreeDatas(true, false);
		if(notOk(depts)) {
			return null;
		}
		List<JsTreeBean> jsTreeBeans=new ArrayList<JsTreeBean>();
		boolean hasRoot = isOk(rootName);
		if (hasRoot) {
			jsTreeBeans.add(new JsTreeBean(0, 0, rootName, openLevel != 0, openLevel == 0 ? KEY_ROOT : KEY_ROOT_OPENED, true));
		}
		for(Dept dept:depts) {
			processDeptJsTree(jsTreeBeans,dept,hasRoot,selectUserId);
		}
		return jsTreeBeans;
	}

	private void processDeptJsTree(List<JsTreeBean> jsTreeBeans,Dept dept, boolean hasRoot,Long selectedId) {
		jsTreeBeans.add(convertToJsTreeBean(dept.toRecord(), true, false, KEY_PARENT, ID, PID, NAME, ENABLE, hasRoot, true));
		List<Dept> sons = dept.getItems();
		if(isOk(sons)) {
			for(Dept son:sons) {
				processDeptJsTree(jsTreeBeans,son,hasRoot,selectedId);
			}
		}else {
			List<User> users = getEnableUserByDept(dept.getId());
			if(isOk(users)) {
				jsTreeBeans.addAll(convertJsTree(users, selectedId, -1, null, ID, "dept_id", "name", ENABLE,SORT_RANK, null, true,p->(dept.getId().longValue()==p.getDeptId().longValue())));
			}
		}
	}

	/**
	 * 获得一个部门下的启用的用户列表
	 * @param deptId
	 * @return
	 */
	public List<User> getEnableUserByDept(Long deptId) {
		return find(selectSql().eq("dept_id", deptId).eq("enable", TRUE).orderById());
	}

	/**
	 * 初始化租户超管
	 * @param tenantId
	 * @param tenantName
	 * @param tenantSn
	 */
	public void initTenantSystemAdminUser(String tenantId,String tenantName,String tenantSn) {
		if(StrKit.hasBlank(tenantId,tenantName,tenantSn)) {
			throw new RuntimeException("租户系统管理员创建需要传入所有参数不为空");
		}
		String tableName = JBoltDbKit.getTenantTable(User.class, tenantId);
		boolean exist = JBoltDbKit.tableExist(JBoltConfig.SAAS_TENANT_DATASOURCE_CONFIG_NAME, tableName);
		if(!exist) {
			throw new RuntimeException("租户用户表尚未生成和初始化，无法为租户创建超管员");
		}
		//总部模拟调用分表
		boolean existSystemAdmin = JBoltSaasTenantKit.me.useById(tenantId).call(()->exists(selectSql().eq("is_system_admin", TRUE)));
		if(existSystemAdmin) {
			LOG.debug("租户:["+tenantName+":"+tenantSn+"][系统User-超管员]已存在 无需再初始化...");
			return;
		}
		LOG.debug("正在初始化租户:["+tenantName+":"+tenantSn+"][系统User-超管员]...");
		User user=new User();
		user.setName("总管理");
		user.setEnable(true);
		user.setPinyin("zgl,zongguanli");
		user.setSex(1);
		user.setUsername(tenantSn+"_admin");
		user.setIsSystemAdmin(true);
		String password=tenantSn+"_123456";
		String pwdSalt=HashKit.generateSaltForSha256();
		user.setPwdSalt(pwdSalt);
		user.setPassword(calPasswordWithSalt(password, pwdSalt));
		user.autoProcessIdValue();
		user.setCreateUserId(0L);
		boolean success=JBoltSaasTenantKit.me.useById(tenantId).call(user::save);
		LOG.debug("初始化租户:["+tenantName+":"+tenantSn+"][系统User-超管员]："+(success?"成功":"失败"));
		if(!success) {
			throw new RuntimeException("初始化租户:["+tenantName+":"+tenantSn+"][系统User-超管员]：失败");
		}
	}



	/**
	 * 设置enable
	 * @param userId
	 * @param value
	 */
	public void changeUserEnable(Long userId, boolean value) {
		if(notOk(userId)) {
			throw new RuntimeException("参数异常:userId");
		}
		User user = findById(userId);
		if(user == null) {
			throw new RuntimeException("参数异常:userId");
		}
		if(value != user.getEnable()) {
			user.setEnable(value);
			boolean success = user.update();
			if(!success) {
				throw new RuntimeException("更新用户启用状态失败");
			}
		}
	}

	/**
	 * 获取首个超管
	 * @return
	 */
	public User getFirstSystemAdmin() {
		return findFirst(Okv.by("is_system_admin", TRUE));
	}

	/**
	 * 获取首个超管的username
	 * @return
	 */
	public String getFirstSystemAdminUserName() {
		User user = getFirstSystemAdmin();
		return user==null?null:user.getUsername();
	}

	/**
	 * 检测用户是否规定时间内没有修改密码 websocket提醒
	 * @return
	 */
	public Kv checkUserLastPwdUpdateTime(Long id){
		int configDays = JBoltGlobalConfigCache.me.getSystemPasswordChangeNoticeDays();
		if(configDays <= 0){return Kv.by("need",false);}
		User user = findById(id);
		if(user == null){
			throw new RuntimeException("获取用户信息异常");
		}
		Date pwdDate = user.getLastPwdUpdateTime();
		if(pwdDate == null){
			pwdDate = user.getCreateTime();
		}
		int days = JBoltDateUtil.daysBetween(pwdDate,new Date());
		return Kv.by("config",configDays).set("days",days).set("need",(days>=configDays));
	}

	/**
	 * 检测用户是否规定时间内没有修改密码 websocket提醒
	 * @return
	 */
	public Kv checkMyLastPwdUpdateTime(){
		return checkUserLastPwdUpdateTime(JBoltUserKit.getUserId());
	}


}
